P/Invoke的全称是Platform Invoke (平台调用),它实际上是一种函数调用机制通 过P/Invoke我们就可以调用非托管DLL中的函数。
P/Invoke依次执行以下操作:
- 查找包含该函数的非托管DLL
- 将该非托管DLL加载到内存中
- 查找函数在内存中的地址并将其参数按照函数的调用约定压栈
- 将控制权转移给非托管函数

由于项目需要用到了C#代码调用C++的动态库,期间遇到了参数封装与传送的问题,记录于此。
- 对于不对称的结构体数组,最好定义紧凑型排列以避免编译器自动做字节对齐。
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
 | [System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential, CharSet = System.Runtime.InteropServices.CharSet.Ansi, Pack=1)] public struct SRawStockDict {          public uint m_dwStockID;          public byte m_cMarket;          public byte m_cType;          [System.Runtime.InteropServices.MarshalAsAttribute(System.Runtime.InteropServices.UnmanagedType.ByValTStr, SizeConst = 23)]     public string m_cCode;          [System.Runtime.InteropServices.MarshalAsAttribute(System.Runtime.InteropServices.UnmanagedType.ByValTStr, SizeConst = 23)]     public string m_cName;          [System.Runtime.InteropServices.MarshalAsAttribute(System.Runtime.InteropServices.UnmanagedType.ByValTStr, SizeConst = 31)]     public string m_cNameEx;          public byte m_cFlag;          public uint m_dwObjID;          public byte m_cOptType; }
 | 
如上,若不加Pack=1。那么用Marshal.Sizeof(typeof(SRawStockDict))获取到的大小是92。加上Pack=1之后得到的大小就是89,这样在传入被调用的C++函数中不会出错。
- 若导出的C++函数中有指向结构体数组的指针,并且该参数既会作为输入也会作为输出。那么类型匹配如:
| 1 2 3 4
 | C++ SRawStockDict *pNew C# [In, Out] SRawStockDict[] pNew
 | 
对于SRawStockDict *&pNew 这种特殊的情况 ,若有必要的话可以把C++函数再封装一层,在内部再做一次赋值。同时调用的时候就做好空间申请。
| 1 2 3 4 5
 | SRawStockDict[] sRawStockDict = new SRawStockDict[20000]; for (int i = 0; i < sRawStockDict.Length; i++)  {       sRawStockDict[i] = new SRawStockDict();  }
 | 
- char[ ]可直接用byte[ ]作为匹配输入。
最后,推荐一个专门用来做P/Invoke参数及类型转换的工具。
P/Invoke Interop Assistant GUI Tool